﻿#include "XVideoWidget.h"
#include <QDebug>
#include <assert.h>

#include <QTimer>

//准备yuv数据
// ffmpeg -i test.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240x128.yuv

#define GET_STR(x) #x

//顶点shader
//顶点着色器的源码
//GLSL
//opengl shading language
const char * vString = GET_STR(
            attribute vec4 vertexIn;
            attribute vec2 textureIn;
            varying vec2 textureOut;
            void main(void)
            {
                gl_Position = vertexIn;
                textureOut = textureIn;
            }
 );

  //片元shader
 const char *fString = GET_STR(
           varying vec2 textureOut;
            uniform sampler2D tex_y;
            uniform sampler2D tex_u;
            uniform sampler2D tex_v;
            void main(void)
              {
                    vec3 yuv;
                    vec3 rgb;
                    yuv.x = texture2D(tex_y, textureOut).r;
                    yuv.y = texture2D(tex_u, textureOut).r - 0.5;
                    yuv.z = texture2D(tex_v, textureOut).r - 0.5;
                    rgb = mat3(1.0, 1.0, 1.0,
                        0.0, -0.39465, 2.03211,
                        1.13983, -0.58060, 0.0) * yuv;
                    gl_FragColor = vec4(rgb, 1.0);
                }

    );




XVideoWidget::XVideoWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    fp=NULL;

    width = 240;
    height = 128;
}

#define ATTRIB_VERTEX 3
#define ATTRIB_TEXTURE 4

//初始化opengl
 void XVideoWidget::initializeGL()
 {
    qDebug() << "initializeGL";
    //初始化QOpenGLFunctions 继承来的数据

    initializeOpenGLFunctions();
    //顶点shader
    bool ret = program.addShaderFromSourceCode(QOpenGLShader::Vertex, vString);
    qDebug() <<"Vertex  " <<ret;
    //片元shader
    ret = program.addShaderFromSourceCode(QOpenGLShader::Fragment, fString);
    qDebug() <<"Fragment  " <<ret;

    //设置顶点坐标的变量， 将顶点绑定到一个位置上, 属性到顶点着色器源码中有声明
    program.bindAttributeLocation("vertexIn",ATTRIB_VERTEX);

    //设置材质标量绑定到一个位置上
    program.bindAttributeLocation("textureIn", ATTRIB_TEXTURE);

    //链接所有添入到的着色器程序
    ret = program.link();
    qDebug() <<"link  " <<ret;
    //激活所有链接
    ret = program.bind();
     qDebug() <<"bind  " <<ret;


     //设置顶点个片元纹理的矩阵坐标

     // 顶点矩阵
      static const GLfloat vertexVertices[] = {
           -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f,
       };
       //纹理矩阵
       static const GLfloat textureVertices[] = {
           0.0f,  1.0f,
           1.0f,  1.0f,
           0.0f,  0.0f,
           1.0f,  0.0f,
       };

       //设置属性ATTRIB_VERTEX的顶点矩阵值以及格式
       glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0,vertexVertices);
       //使其生效
       glEnableVertexAttribArray(ATTRIB_VERTEX);

       glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0,textureVertices);

       glEnableVertexAttribArray(ATTRIB_TEXTURE);


       //读取着色器中的数据变量tex_y, tex_u, tex_v的位置,这些变量的声明可以在
       //片段着色器源码中可以看到
       textureUniformY = program.uniformLocation("tex_y");
       textureUniformU = program.uniformLocation("tex_u");
       textureUniformV = program.uniformLocation("tex_v");

       //创建材质
       glGenTextures(3, texs);

       //Y
       glBindTexture(GL_TEXTURE_2D, texs[0]);
       //放大过滤，线性插值
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
       //创建材质显卡空间
       glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

       //U
       glBindTexture(GL_TEXTURE_2D, texs[1]);
       //放大过滤，线性插值
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        //创建材质显卡空间
       glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

       //V
       glBindTexture(GL_TEXTURE_2D, texs[2]);
       //放大过滤，线性插值
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
       //创建材质显卡空间
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

        ///分配材质内存空间
        datas[0] = new unsigned char[width*height];		// Y
        datas[1] = new unsigned char[width*height/4];	//U
        datas[2] = new unsigned char[width*height/4];	//V


        fp = fopen("out240x128.yuv", "rb");
        if(NULL == fp)
        {
            assert(false);
        }

        //启动定时器
        QTimer *ti = new QTimer(this);
        connect(ti, SIGNAL(timeout()), this, SLOT(update()));
        ti->start(40);
 }
 //窗口发生变化
 void XVideoWidget::resizeGL(int w, int h)
 {
    qDebug() << "resizeGL w:" <<w <<" h:" <<h;
 }
 //刷新显示的
 void XVideoWidget::paintGL()
 {
    if(feof(fp))
    {
       fseek(fp, 0, SEEK_SET);
    }

    fread(datas[0], 1, width*height, fp);
     fread(datas[1], 1, width*height/4, fp);
      fread(datas[2], 1, width*height/4, fp);

    //Y
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texs[0]);

    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);

    glUniform1i(textureUniformY, 0);

    //U
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texs[1]);

    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);

    glUniform1i(textureUniformU, 1);


    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, texs[2]);

    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[2]);

    glUniform1i(textureUniformV, 2);


    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    qDebug() << "paintGL";
 }
